﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace XRoute
{
    [TemplatePart(Name = RouteDrawing_PartName, Type = typeof(DrawingHost))]
    public class RouteImage : Control
    {
        ///////////////////////////////////////////////////////////////////////

        const int MapScaleFactor = 4;
        const string RouteDrawing_PartName = "PART_RouteDrawing";

        Model.Map _map;
        Model.Node _origin;
        DrawingHost _routeDrawing;

        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.Register("Source", typeof(BitmapSource), typeof(RouteImage),
                new PropertyMetadata(
                    null,
                    (obj, e) => ((RouteImage)obj).OnSourcePropertyChanged((BitmapSource)e.NewValue)));

        static readonly DependencyPropertyKey RoutePropertyKey =
            DependencyProperty.RegisterReadOnly("Route", typeof(Model.Route), typeof(RouteImage),
                new PropertyMetadata(
                    null,
                    (obj, e) => ((RouteImage)obj).DrawRoute()));

        public static readonly DependencyProperty RouteProperty = RoutePropertyKey.DependencyProperty;


        ///////////////////////////////////////////////////////////////////////

        public RouteImage()
        {
            MouseLeftButtonDown += RouteImage_MouseLeftButtonDown;
        }

        public BitmapSource Source
        {
            get { return GetValue(SourceProperty) as BitmapSource; }
            set { SetValue(SourceProperty, value); }
        }

        internal Model.Route Route
        {
            get { return GetValue(RouteProperty) as Model.Route; }
            private set { SetValue(RoutePropertyKey, value); }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _routeDrawing = Template.FindName(RouteDrawing_PartName, this) as DrawingHost;

            Route = null;
            DrawRoute();
        }


        ///////////////////////////////////////////////////////////////////////

        static RouteImage()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(RouteImage), new FrameworkPropertyMetadata(typeof(RouteImage)));
        }

        void RouteImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var position = e.GetPosition(this);

            if (_origin == null)
            {
                _origin = GetNodeAt(position);
            }
            else
            {
                var destination = GetNodeAt(position);

                if (destination != null)
                {
                    var route = _map.FindRoute(_origin, destination);

                    if (route != null)
                        Route = route;

                    _origin = null;
                }
            }
        }

        void OnSourcePropertyChanged(BitmapSource bitmap)
        {
            var mapBitmap = ResizeBitmap(bitmap, bitmap.PixelWidth / MapScaleFactor, bitmap.PixelHeight / MapScaleFactor);
            _map = Model.Map.Create(mapBitmap);
            Route = null;
        }

        void DrawRoute()
        {
            if (_routeDrawing == null)
                return;

            var route = Route;

            if (route == null)
            {
                _routeDrawing.ClearDrawing();
                return;
            }

            var geometry = new StreamGeometry();

            using (var ctx = geometry.Open())
            {
                var first = true;

                foreach (var node in route)
                {
                    var point = new Point(
                        node.X * MapScaleFactor + MapScaleFactor / 2.0,
                        node.Y * MapScaleFactor + MapScaleFactor / 2.0);

                    if (first)
                    {
                        ctx.BeginFigure(point, isFilled: false, isClosed: false);
                        first = false;
                    }
                    else
                    {
                        ctx.LineTo(point, isStroked: true, isSmoothJoin: true);
                    }
                }
            }

            _routeDrawing.RenderDrawing(dc => dc.DrawGeometry(Foreground, new Pen(Foreground, 5), geometry));
        }

        Model.Node GetNodeAt(Point point)
        {
            var x = (int)Math.Round(point.X / MapScaleFactor);
            var y = (int)Math.Round(point.Y / MapScaleFactor);
            
            if (x >= 0 && x < _map.Columns
            &&  y >= 0 && y < _map.Rows)
                return _map[x, y];

            return null;
        }

        static BitmapSource ResizeBitmap(BitmapSource bitmap, double width, double height)
        {
            var scaledBitmap = new TransformedBitmap(bitmap,
                new ScaleTransform(
                    width / bitmap.PixelWidth,
                    height / bitmap.PixelHeight));

            scaledBitmap.Freeze();

            var convertedBitmap = new FormatConvertedBitmap(scaledBitmap, PixelFormats.Bgra32, null, 0.0);
            convertedBitmap.Freeze();

            return convertedBitmap;
        }
    }
}
